package edu.cmu.vlis.datamining.utils;

import java.io.File;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.zip.GZIPInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.jgrapht.VertexFactory;
import org.jgrapht.graph.ClassBasedEdgeFactory;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException;

import edu.cmu.vlis.datamining.core.Edge;
import edu.cmu.vlis.datamining.core.Vertex;



public class ParserPostPost {

	//ListenableDirectedWeightedGraph g;
	DefaultDirectedWeightedGraph<Vertex,Edge > graph;
	Map <String,Vertex> vertexMap;
	Map <Integer,String> blogPostMap;
	Integer uniqueId = new Integer(1);
	int numEdges= 0;
	int noLinksCount = 0;
	int badBlogsCount=0;
	int totBlogs =0;
	BufferedReader reader;
	DocumentBuilder builder;
	Document doc;
	VertexFactory<Vertex> vertexFac = null;

	public ParserPostPost() {

		this.vertexMap = new HashMap<String, Vertex>();
		this.blogPostMap = new HashMap<Integer,String>();
		
		this.graph =     new DefaultDirectedWeightedGraph<Vertex,Edge>(new ClassBasedEdgeFactory<Vertex,Edge>(Edge.class));

		//Create a document Parser
		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true); 
		try {
			this.builder = domFactory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}

	}

	public void processBlog(String xmlBlog){

		//System.out.println("Starting processing of: "+xmlBlog);
		try {
			InputSource inStream = new InputSource();
			inStream.setCharacterStream(new StringReader(xmlBlog));
			this.doc = this.builder.parse(inStream);
		}
		catch (MalformedByteSequenceException im) {
			System.err.println("MalformedByteSequenceException");
			badBlogsCount++;
			return;
		}catch (SAXException e) {
			System.err.println("SAXException");
			badBlogsCount++;
			return;
		} catch(IOException e){
			System.err.println("IOException");
			badBlogsCount++;
			return;
		}
		this.doc.getDocumentElement().normalize();
		try {
			//Retrieve Blog Url
			String blogUrl = doc.getElementsByTagName("weblog_url").item(0).getChildNodes().item(0).getNodeValue().trim();

			//Retrive Post url
			String postUrl =doc.getElementsByTagName("permalink").item(0).getChildNodes().item(0).getNodeValue().trim();

			//Retrieve Blog Title
			NodeList nodeList = doc.getElementsByTagName("weblog_title").item(0).getChildNodes();
			String webLogTitle ="";
			if(nodeList.getLength()!=0) 
				webLogTitle = nodeList.item(0).getNodeValue().trim();

			//Retrieve BlogPost Title 
			nodeList = doc.getElementsByTagName("post_title").item(0).getChildNodes();
			String blogPostTitle = "";
			if(nodeList.getLength()!=0) 
				blogPostTitle = nodeList.item(0).getNodeValue().trim();


			//Retrieve Blog AuthorName
			nodeList = doc.getElementsByTagName("author_name").item(0).getChildNodes();
			String authorName="";
			if(nodeList.getLength()!=0) 
				authorName = nodeList.item(0).getNodeValue().trim();

			//Retrieve Blog Date Posted
			nodeList = doc.getElementsByTagName("date_posted").item(0).getChildNodes();
			String dateStr="";
			Date blogDate = null;
			if(nodeList.getLength()!=0)  {
				dateStr = nodeList.item(0).getNodeValue().trim();

				DateFormat dateFormat = new SimpleDateFormat("yyyymmdd");

				try {
					blogDate = dateFormat.parse(dateStr);
				} catch (ParseException e) {
					System.err.println("Invalid Date Parser Exception ");
					e.printStackTrace();
				}
			}

			//Get the outlinks of the blog
			nodeList = doc.getElementsByTagName("outlink");

			if(nodeList.getLength()==0)
				this.noLinksCount ++;
			else{

				//create Vertex for this Post
				Vertex v;
				if(vertexMap.containsKey(postUrl)) {
					v = vertexMap.get(postUrl);
				}else {
					v = new Vertex(uniqueId,postUrl);
					//Set all attributes for the vertex
					v.setBlogPostTitle(blogPostTitle);
					v.setWebLogTitle(webLogTitle);
					v.setAuthorName(authorName);
					v.setDatePosted(blogDate);
					v.setSupplementaryUrl(blogUrl);
					vertexMap.put(postUrl, v);
					this.blogPostMap.put(uniqueId,blogUrl);
					uniqueId++;
					//System.out.println("added a vertex!" + vertexMap.size());
					graph.addVertex(v);
				}	

				//Create the edges
				numEdges += nodeList.getLength();
				for(int i=0;i<nodeList.getLength();i++) {
					Node outLinkNode = nodeList.item(i);
					if(outLinkNode.getNodeType() == Node.ELEMENT_NODE){
						Element el = (Element)outLinkNode;

						String outUrl = el.getElementsByTagName("url").item(0).getChildNodes().item(0).getNodeValue().trim();
						String outSite =  el.getElementsByTagName("site").item(0).getChildNodes().item(0).getNodeValue().trim();
						String outType = el.getElementsByTagName("type").item(0).getChildNodes().item(0).getNodeValue().trim();

						//Check if url vertex already exists in the Else Create it
						Vertex vOut =null;
						if(vertexMap.containsKey(outUrl)) {
							vOut = vertexMap.get(outUrl);
						}else {
							vOut = new Vertex(uniqueId,outUrl);
							vOut.setSupplementaryUrl(outSite);
							vOut.setType(outType);
							vertexMap.put(outUrl,vOut);
							this.blogPostMap.put(uniqueId,blogUrl);
							uniqueId++;
							graph.addVertex(vOut);
						}	
						//System.out.println("Added a edge" + numEdges);
						graph.addEdge(v, vOut);
					}
				}
			}
		}catch (Exception e) {
			System.out.println(e.toString());
			return;
		}


	}

	public void processGzippedFile(File file) {
		try {
			this.reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
			String line;
			boolean flag_on=false;
			String xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
			String xmlBlog = xmlHeader;
			int i=0;
			while ((line = reader.readLine()) != null) {
				if (line.contains("<post>")) {  //start building the message
					flag_on = true;
				}
				if(flag_on)
					xmlBlog = xmlBlog.concat(line);
				if(line.contains("</post>")) {
					//process the XMl message as it is one blog
					processBlog(xmlBlog);
					xmlBlog = xmlHeader;
					i++;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
			 return;
		} 
	}

	public void printStats() {

		System.out.println("Total number of Vertices/blogs in graph " + vertexMap.size());
		System.out.println("Totalnumber of Edges/connections  in the graph " + this.numEdges);
		System.out.println("Number of blogs not having any outlinks  " + this.noLinksCount);
		System.out.println("Number of blogs not processed  " + this.badBlogsCount);
	}

	public void parse(File dir) {

		//Read the files in directory data
		//ParserPostPost parser = new ParserPostPost();
		File[] gzippedFiles = dir.listFiles();
		//System.out.println(gzippedFiles.length);
		for(File gzippedFile : gzippedFiles){
			System.out.println("Starting processing of "+gzippedFile.getAbsolutePath());
			processGzippedFile(gzippedFile);
		}
		System.out.println("Done processing all gzipped files.");
	}

	/**
	 * @return the graph
	 */
	public DefaultDirectedWeightedGraph<Vertex, Edge> getGraph() {
		return this.graph;
	}

	public Map<String, Vertex> getVertexMap() {
		return vertexMap;
	}
	
	public Map<Integer,String> getBlogPostMap() {
		return this.blogPostMap;
	}
}
